package com.kamistoat.meimeimall.meimeimallproduct.service.impl;

import com.kamistoat.meimeimall.common.constant.DatabaseConstant;
import com.kamistoat.meimeimall.common.utils.PageUtils;
import com.kamistoat.meimeimall.common.utils.Query;
import com.alibaba.fastjson.JSON;
import com.kamistoat.meimeimall.common.To.EsTo.SpuUpTo;
import com.kamistoat.meimeimall.common.To.seckillTo.SecKillProductCacheTo;
import com.kamistoat.meimeimall.common.To.seckillTo.SecKillSkuInfoTo;
import com.kamistoat.meimeimall.common.To.wareTo.HasStockMapTo;
import com.kamistoat.meimeimall.common.dubboInterface.SearchDubboInterface;
import com.kamistoat.meimeimall.common.dubboInterface.SecKillDubboInterface;
import com.kamistoat.meimeimall.common.dubboInterface.WareDubboInterface;
import com.kamistoat.meimeimall.common.exception.BizCodeEnum;
import com.kamistoat.meimeimall.common.exception.RRException;
import com.kamistoat.meimeimall.common.utils.R;
import com.kamistoat.meimeimall.meimeimallproduct.dao.*;
import com.kamistoat.meimeimall.meimeimallproduct.entity.*;
import com.kamistoat.meimeimall.meimeimallproduct.service.*;
import com.kamistoat.meimeimall.meimeimallproduct.vo.thymeleaf.skuItem.SkuItemSaleAttrVo;
import com.kamistoat.meimeimall.meimeimallproduct.vo.thymeleaf.skuItem.SkuItemVo;
import com.kamistoat.meimeimall.meimeimallproduct.vo.thymeleaf.skuItem.SpuItemBaseAttrGroupVo;
import org.apache.dubbo.config.annotation.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;


@Service("skuInfoService")
public class SkuInfoServiceImpl extends ServiceImpl<SkuInfoDao, SkuInfoEntity> implements SkuInfoService {
    protected Logger logger = LoggerFactory.getLogger(SkuInfoServiceImpl.class);

    @Autowired
    SkuImagesDao skuImagesDao;
    @Autowired
    SkuImagesService skuImagesService;
    @Autowired
    SpuInfoDescService spuInfoDescService;
    @Autowired
    SkuSaleAttrValueDao skuSaleAttrValueDao;
    @Autowired
    SkuSaleAttrValueService skuSaleAttrValueService;
    @Autowired
    AttrGroupService attrGroupService;
    @Autowired
    ProductAttrValueService productAttrValueService;
    @Autowired
    SpuInfoDao spuInfoDao;
    @Autowired
    AttrService attrService;
    @Autowired
    AttrDao attrDao;
    @Autowired
    BrandService brandService;
    @Autowired
    CategoryService categoryService;
    @Autowired
    SkuInfoDao skuInfoDao;

    @Reference
    SearchDubboInterface searchDubboInterface;
    @Reference
    SecKillDubboInterface secKillDubboInterface;
    @Reference
    WareDubboInterface wareDubboInterface;

    @Autowired
    ThreadPoolExecutor executorPool;  // 自动注入我们配置的Bean线程池

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<SkuInfoEntity> page = this.page(
                new Query<SkuInfoEntity>().getPage(params),
                new QueryWrapper<SkuInfoEntity>()
        );

        return new PageUtils(page);
    }

    /**
     * 并非服务层接口方法，只是在saveSPUInfo()中调用
     *
     * @param skuInfoEntity
     */
    @Override
    public void saveSkuInfo(SkuInfoEntity skuInfoEntity) {
        baseMapper.insert(skuInfoEntity);
    }

    /**
     * 自己编写的方法，查询SKU信息。
     *
     * @param params 查询条件，包括菜单Id、品牌Id、min、max
     * @return 分页封装
     */
    @Override
    public PageUtils queryPageBySkuInfo(Map<String, Object> params) {
        QueryWrapper<SkuInfoEntity> wrapper = new QueryWrapper<>();
        if (!StringUtils.isEmpty(params.get("catelogId")) && !params.get("catelogId").equals("0")) {
            wrapper.eq("catalog_id", params.get("catelogId"));
        }
        if (!StringUtils.isEmpty(params.get("brandId")) && !params.get("brandId").equals("0")) {
            wrapper.eq("brand_id", params.get("brandId"));
        }
        // 页面默认min和max都是0，只要两个有一个不为0，就对价格进行筛选
        // 前端限制了max不小于min，所以后端这里一定是有意义的
        if (!params.get("min").equals("0") || !params.get("max").equals("0")) {
            // 直接用wrapper的库条件 .ge表示大于等于
            // .le表示小于等于
            wrapper.ge("price", params.get("min"));
            wrapper.le("price", params.get("max"));
            // 这里也可以对String类型的min和max强转成BigDecimal，
            // 并用try-catch来捕捉异常，这样可以避免前端提交的min和max是字母的情况
//            try {
//                BigDecimal min = new BigDecimal((String)params.get("min"));
//                BigDecimal max = new BigDecimal((String)params.get("max"));
//                // 依然可以用wrapper.ge/.le来快速判断
//                wrapper.ge("price",min);
//                wrapper.le("price",max);
//            }catch (Exception e){
//                log.error(e.getMessage());
//            }

        }
        if (!StringUtils.isEmpty(params.get("key"))) {
            wrapper.and(obj -> {
                obj.eq("sku_id", params.get("key")).or().
                        like("sku_name", params.get("key")).or().
                        like("sku_desc", params.get("key"));
            });
        }
        IPage<SkuInfoEntity> skuInfoEntityIPage = this.page(new Query<SkuInfoEntity>().getPage(params), wrapper);
        PageUtils pageUtils = new PageUtils(skuInfoEntityIPage);
        return pageUtils;
    }

    /**
     * 构建商品详情页信息。
     *
     * @param skuId skuID
     * @return VO类
     */
    @Override
    public SkuItemVo getSkuItem(Long skuId) {
        SkuItemVo skuItemVo = new SkuItemVo();

        // 异步编排
        // 第一个应该是查sku基本信息，查询结果之后的异步任务还要用，所以应该使用supplyAsync方法，返回查询结果
        CompletableFuture<SkuInfoEntity> skuInfoFuture = CompletableFuture.supplyAsync(() -> {
            SkuInfoEntity info = this.getById(skuId);
            skuItemVo.setSkuInfo(info);
            return info;
        }, executorPool);

        // 查完sku基本信息后，就可以执行后面的异步。因为要接收第一个异步的返回值，所以用thenAccept
        CompletableFuture<Void> saleAttrFuture = skuInfoFuture.thenAcceptAsync((infoResult) -> {
            /**
             * 设置sku所属的spu的所有销售属性的值
             */
            List<SkuItemSaleAttrVo> skuItemSaleAttrVoList = skuSaleAttrValueService.getSpuSkuSaleAttrsBySpuId(infoResult.getSpuId());
            skuItemVo.setSpuSkuSaleAttrs(skuItemSaleAttrVoList);
        }, executorPool);

        // 和上面的异步是同一时间的，都是在查完sku基本信息之后
        CompletableFuture<Void> spuDescFuture = skuInfoFuture.thenAcceptAsync((infoResult) -> {
            /**
             * spu的介绍信息
             */
            skuItemVo.setSpuInfoDesc(spuInfoDescService.getById(infoResult.getSpuId()));  // 长String地址拼串，前端记得拆串
        }, executorPool);

        // 和上面的异步是同一时间的，都是在查完sku基本信息之后
        CompletableFuture<Void> baseAttrFuture = skuInfoFuture.thenAcceptAsync((infoResult) -> {
            /**
             * 查询spu的基础属性分组，方法在别的服务中写
             */
            List<SpuItemBaseAttrGroupVo> spuItemBaseAttrGroupVoList =
                    attrGroupService.getAttrGroupWithAttrBySpuId(infoResult.getSpuId(), infoResult.getCatalogId());
            skuItemVo.setSpuBaseAttrGroups(spuItemBaseAttrGroupVoList);
        }, executorPool);

        // 这个异步就随便时候了。因为没有返回值，用run就行了
        CompletableFuture<Void> imageFuture = CompletableFuture.runAsync(() -> {
            skuItemVo.setSkuImages(skuImagesService.list(new QueryWrapper<SkuImagesEntity>().eq("sku_id", skuId)));
        }, executorPool).thenRunAsync(() -> {
            List<Long> idList = new ArrayList<>();
            idList.add(skuId);
            R hasStockR = wareDubboInterface.hasStock(idList);
            if (hasStockR.getCode() == 0) {
                HasStockMapTo hasStockMapTo = JSON.parseObject((String) hasStockR.get("hasStockMapTo"), HasStockMapTo.class);
                Map<Long, Boolean> hasStockMap = hasStockMapTo.getHasStockMap();
                skuItemVo.setHasStock(hasStockMap.get(skuId));
            }
        }, executorPool);

        // 查询商品未来三天秒杀活动。同样在任意时刻执行异步任务即可。
        CompletableFuture<Void> secKillFuture = CompletableFuture.runAsync(() -> {
            R secKillInfoThreeDaysR = secKillDubboInterface.getSecKillInfoThreeDays(skuId);
            if (secKillInfoThreeDaysR.getCode() != 0) {
                // 只要商品存在秒杀活动，SkuItemVo中该变量的值就非空。否则就是空
                SecKillProductCacheTo cacheTo = JSON.parseObject((String) secKillInfoThreeDaysR.get("secKillInfoThreeDays"), SecKillProductCacheTo.class);
                if (cacheTo != null) {
                    skuItemVo.setSecKillProductCacheTo(cacheTo);
                }
            } else {
                throw new RRException(BizCodeEnum.SECKILL_SEARCH_FAIL.getMessage(), BizCodeEnum.SECKILL_SEARCH_FAIL.getCode());
            }
        }, executorPool);

        try {
            // 等待所有任务都完成才能返回，当然如果我想，我完全可以捕获异常进行处理。这里直接抛出就不处理了
            CompletableFuture.allOf(skuInfoFuture, saleAttrFuture, baseAttrFuture, imageFuture, spuDescFuture, secKillFuture).get();
            return skuItemVo;
        } catch (Exception exception) {
            throw new RRException(exception.getMessage(), BizCodeEnum.UNKNOW_EXCEPTION.getCode());
        }
    }

    /**
     * 对单个sku添加到ES
     *
     * @param skuId
     * @return
     */
    @Override
    @Transactional
    public void skuUpToES(Long skuId) {
        SkuInfoEntity infoEntity = this.getById(skuId);
        // 查出spu对应的基础属性，返回一个list，因为所有的spuUpTo用的都是相同的，所以可以提前出来做
        List<ProductAttrValueEntity> productAttrValueEntityList =  // spu与Attr的关系直接是有一张数据表存放的
                productAttrValueService.list(
                        new QueryWrapper<ProductAttrValueEntity>().eq("spu_id", infoEntity.getSpuId()));
        // 流处理返回一个List<spuAttrs>，直接保存到每个spuUpTo中
        // 注意，还涉及到一个判断属性是否为 “可检索属性”
        List<SpuUpTo.spuAttrs> spuAttrsList = productAttrValueEntityList.stream().map(productAttrValueEntity -> {
            SpuUpTo.spuAttrs spuAttrs = new SpuUpTo.spuAttrs();
            spuAttrs.setAttrId(productAttrValueEntity.getAttrId());
            spuAttrs.setAttrName(productAttrValueEntity.getAttrName());
            // 数据库中 值;值;值 形式的String数据，要分割后处理成列表
            List<SpuUpTo.attrValue> attrValueList = Arrays.asList(productAttrValueEntity.getAttrValue().split(";")).stream().map(value -> {
                SpuUpTo.attrValue attrValue = new SpuUpTo.attrValue();
                attrValue.setAttrValue(value);
                return attrValue;
            }).collect(Collectors.toList());
            spuAttrs.setAttrValues(attrValueList);
            // BeanUtils.copyProperties(productAttrValueEntity, spuAttrs);
            return spuAttrs;
        }).filter(spuAttrs -> {
            // 再加一个filter，将attr数据表中searchtype=0的删除掉
            return attrService.getById(spuAttrs.getAttrId()).getSearchType() == 1;
        }).collect(Collectors.toList());

        // 先根据spuId查出一个SkuEntity列表
        List<SkuInfoEntity> skuList = new ArrayList<>();
        skuList.add(infoEntity);
        // 获得skuId列表
        List<Long> skuIdList = new ArrayList<>();
        skuIdList.add(infoEntity.getSkuId());
        // 调用远程接口，一次性查询当前spu下的sku是否有库存。
        // 最好不要在下面的stream中查，调用多次远程接口会拖慢速度
        // 用一个try防止出现异常
        R hasStockR = wareDubboInterface.hasStock(skuIdList);
        Map<Long, Boolean> hasStockMap = null;
        if (hasStockR.getCode() == 0) {
            HasStockMapTo hasStockMapTo = JSON.parseObject((String) hasStockR.get("hasStockMapTo"), HasStockMapTo.class);
            hasStockMap = hasStockMapTo.getHasStockMap();
        }
        // 制作 List<SpuUpTo>
        Map<Long, Boolean> finalSkuHasStock = hasStockMap;
        List<SpuUpTo> spuUpToList = skuList.stream().map(skuInfoEntity -> {
            SpuUpTo spuUpTo = new SpuUpTo();
            BeanUtils.copyProperties(skuInfoEntity, spuUpTo);  // 先将对应的属性复制
            // 需要单独处理：BrandName/BrandImg/CatalogName/Attrs/hasStock/hotScore
            BrandEntity brand = brandService.getById(spuUpTo.getBrandId());  // 查出品牌对应的信息
            spuUpTo.setBrandName(brand.getName());
            spuUpTo.setBrandImg(brand.getLogo());
            CategoryEntity catelog = categoryService.getById(spuUpTo.getCatalogId());  // 查出三级分类信息
            spuUpTo.setCatalogName(catelog.getName());
            spuUpTo.setAttrs(spuAttrsList);  // 保存属性列表
            if (finalSkuHasStock == null) {  // 保存库存信息
                spuUpTo.setHasStock(true);
            } else {
                spuUpTo.setHasStock(finalSkuHasStock.get(spuUpTo.getSkuId()));
            }
            // TODO 热度评分，需要算法实现
            spuUpTo.setHotScore(0L);
            return spuUpTo;
        }).collect(Collectors.toList());

        R spuUpToESR = searchDubboInterface.spuUpToES(spuUpToList);
        if (spuUpToESR.getCode() != 0) {
            throw new RRException(spuUpToESR.getMsg(), spuUpToESR.getCode());
        }
    }

    /**
     * 构建秒杀时需要的 secKillSkuInfoTo
     *
     * @param skuId
     */
    @Override
    public SecKillSkuInfoTo getSkuInfoForSecKill(Long skuId) {
        SkuInfoEntity skuInfoEntity = this.getById(skuId);
        SecKillSkuInfoTo secKillSkuInfoTo = new SecKillSkuInfoTo();
        BeanUtils.copyProperties(skuInfoEntity, secKillSkuInfoTo);
        secKillSkuInfoTo.setBrandName(brandService.getBrandName(skuInfoEntity.getBrandId()));
        secKillSkuInfoTo.setSkuAttr(skuSaleAttrValueService.getStringAttr(skuId));
        return secKillSkuInfoTo;
    }

    /**
     * 构建测试数据
     *
     * @param skuPerSpu
     */
    @Override
    public void constructTestSkuData(Integer skuPerSpu) {
        List<SpuInfoEntity> spuInfos = spuInfoDao.selectAllSpu();
        logger.info("为{}个Spu商项生成{}个Sku商品", spuInfos.size(), spuInfos.size() * skuPerSpu);
        List<SkuInfoEntity> skuInfos = new ArrayList<>();
        List<SkuImagesEntity> images = new ArrayList<>();
        List<SkuSaleAttrValueEntity> saleAttrs = new ArrayList<>();

        for (SpuInfoEntity spuInfo : spuInfos) {
            for (int skuIndex = 1; skuIndex <= skuPerSpu; skuIndex++) {
                SkuInfoEntity var1 = new SkuInfoEntity();
                var1.setSpuId(spuInfo.getId());
                var1.setSkuName(spuInfo.getSpuName() + "Sku商品" + skuIndex);
                var1.setSkuDesc(spuInfo.getSpuName() + "Sku商品" + skuIndex + "描述");
                var1.setCatalogId(spuInfo.getCatalogId());
                var1.setBrandId(spuInfo.getBrandId());
                var1.setSkuDefaultImg("http://test.pic");
                var1.setSkuTitle(spuInfo.getSpuName() + "Sku商品" + skuIndex + "标题");
                var1.setSkuSubtitle(spuInfo.getSpuName() + "Sku商品" + skuIndex + "副标题");
                int front = (int) (Math.random() * 5000);
                var1.setPrice(new BigDecimal(front + ".00"));
                var1.setCreateTime(new Date());
                var1.setUpdateTime(new Date());
                var1.setDelete(DatabaseConstant.UN_LOGISTIC_DELETED);
                skuInfos.add(var1);
            }
        }
        skuInfoDao.insertBatch(skuInfos);
        logger.info("生成{}条SkuInfo", spuInfos.size() * skuPerSpu);

        for (SkuInfoEntity skuInfo : skuInfos) {
            List<AttrEntity> categorySaleAttrs = attrDao.selectSaleAttrByCategoryId(skuInfo.getCatalogId());
            AttrEntity random1 = categorySaleAttrs.get((int) (Math.random() * categorySaleAttrs.size()));
            SkuSaleAttrValueEntity var2 = new SkuSaleAttrValueEntity();
            var2.setSkuId(skuInfo.getSkuId());
            var2.setAttrId(random1.getAttrId());
            var2.setAttrName(random1.getAttrName());
            var2.setAttrValue(random1.getValueSelect().split(";")[(int) (Math.random() * 3)]);
            var2.setCreateTime(new Date());
            var2.setUpdateTime(new Date());
            var2.setDelete(DatabaseConstant.UN_LOGISTIC_DELETED);
            saleAttrs.add(var2);

            AttrEntity random2 = categorySaleAttrs.get((int) (Math.random() * categorySaleAttrs.size()));
            SkuSaleAttrValueEntity var3 = new SkuSaleAttrValueEntity();
            var3.setSkuId(skuInfo.getSkuId());
            var3.setAttrId(random2.getAttrId());
            var3.setAttrName(random2.getAttrName());
            var3.setAttrValue(random2.getValueSelect().split(";")[(int) (Math.random() * 3)]);
            var3.setCreateTime(new Date());
            var3.setUpdateTime(new Date());
            var3.setDelete(DatabaseConstant.UN_LOGISTIC_DELETED);
            saleAttrs.add(var3);

            SkuImagesEntity var4 = new SkuImagesEntity();
            var4.setSkuId(skuInfo.getSkuId());
            var4.setDefaultImg(1);
            var4.setImgUrl("http://test.pic");
            var4.setCreateTime(new Date());
            var4.setUpdateTime(new Date());
            var4.setDelete(DatabaseConstant.UN_LOGISTIC_DELETED);
            images.add(var4);
        }
        skuImagesDao.insertBatch(images);
        skuSaleAttrValueDao.insertBatch(saleAttrs);
        logger.info("生成{}条SkuInfo的配套数据", spuInfos.size() * skuPerSpu);
    }

}